feat(cli): add label and featured commands - #78
Conversation
Expose /v1/labels/ CRUD and /v1/me/featured/ list/set/batch via dailybot label and dailybot featured with table output, error mapping, and unit tests. Co-authored-by: Cursor <cursoragent@cursor.com>
Add dashboard enrichment query params for forms, workflows, and check-ins with serialization tests for CORE-2362. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ndling in label commands - Simplified the formatting of API request calls in `api_client.py` for better readability. - Enhanced error handling in `_parse_entity_type` function in `featured.py` by consolidating the raise statement. - Improved readability of the confirmation prompt in `label.py` for label deletion. Co-authored-by: Cursor <cursoragent@cursor.com>
- Updated assertions in `env_commands_test.py` and `repo_env_test.py` to join whitespace in captured output, ensuring consistent matching for profile status messages.
Give the CLI the same replace-set picker as the web app, plus batch add/remove, and document it for agents. Co-authored-by: Cursor <cursoragent@cursor.com>
2e1b02b to
44e7f8f
Compare
AI review for
|
There was a problem hiding this comment.
Verdict
Solid Labels/Featured command surface and client wiring, but client-side plan gating contradicts the entitlement contract, shared error codes are Labels-branded globally, and list enrichment never reaches the Click list commands users actually run.
Findings
| # | Severity | File | Summary |
|---|---|---|---|
| 1 | dailybot_cli/commands/label.py:79 |
label entitlement is short-circuited by enforce_plan_access |
|
| 2 | dailybot_cli/commands/label.py:325 |
assign/batch omit plan gating that every other label subcommand uses |
|
| 3 | dailybot_cli/commands/public_api_helpers.py:123 |
Generic API codes mapped to Labels-only copy | |
| 4 | dailybot_cli/api_client.py:1006 |
Enrichment params not exposed on form/checkin/workflow list CLIs |
|
| 5 | dailybot_cli/commands/label.py:106 |
--archived help says "include" but sends is_archived filter |
|
| 6 | dailybot_cli/commands/label.py:263 |
--json alone skips hard-delete confirmation |
|
| 7 | ℹ️ info | .agents/skills/dailybot/SKILL.md:54 |
Header says fourteen capabilities; table has fifteen |
| 8 | ℹ️ info | .agents/skills/dailybot/labels/SKILL.md:18 |
Staging host disagrees with the rest of the repo |
| 9 | ℹ️ info | .agents/skills/dailybot/labels/SKILL.md:12 |
Ships "personalized-labels branch" requirement |
| 10 | ℹ️ info | dailybot_cli/commands/label.py:269 |
Abort exits 0 instead of EXIT_USER_ABORTED |
| 11 | ℹ️ info | tests/dashboard_enrichment_list_perf_test.py:9 |
Untyped dict (AGENTS.md Rule 2) |
| 12 | ℹ️ info | dailybot_cli/display.py:193 |
Archived=yes rendered in green |
Notes (no inline anchor / cross-cutting)
- Docs gap:
README.md,docs/API_REFERENCE.md, and.agents/docs/skills_agents_catalog.mdare unchanged — AGENTS.md "New command" maintenance expects README + API docs, and the newdailybot-labelssub-skill is missing from the catalog table (router was updated). - Test gap:
list_checkinsgained enrichment kwargs buttests/dashboard_enrichment_list_perf_test.pyonly covers forms/workflows; no client tests for the new/v1/labels/or/v1/me/featured/methods. - Praise: Entity-type aliasing (
automations→workflows) is tested; assign replace-set /--clearmatch the web picker contract described in the skill; display helpers stay out of Click callbacks.
Recommendation: request-changes
| @click.option("--json", "json_mode", is_flag=True, help="Emit machine-readable JSON to stdout.") | ||
| def label_entitlement(json_mode: bool) -> None: | ||
| """Show Labels entitlement flags for your org.""" | ||
| enforce_plan_access("label_entitlement", json_mode=json_mode) |
There was a problem hiding this comment.
Plan short-circuit blocks the entitlement diagnostic.
enforce_plan_access("label_entitlement") exits free-tier orgs with plan_upgrade_required before GET /v1/labels/entitlement/ runs. Failure mode: a free (or mis-cached) org runs dailybot label entitlement — as .agents/skills/dailybot/labels/SKILL.md instructs, calling entitlement the source of truth — and never sees entitled / can_create flags.
Drop enforce_plan_access from label entitlement (keep server as source of truth). If CRUD stays client-gated, gate on entitlement flags or server codes instead of assuming every free tier lacks Labels.
| if not clear and not label_uuids: | ||
| raise click.UsageError("Pass at least one --label, or --clear to detach all.") | ||
|
|
||
| client = require_auth() |
There was a problem hiding this comment.
Inconsistent plan gating vs other label subcommands.
list / get / create / update / archive / delete / entitlement all call enforce_plan_access; assign (and batch) skip it and go straight to require_auth(). Failure mode: on a known-free org, dailybot label list is client-blocked while dailybot label assign … still hits the API (403 round-trip or surprising success if the server allows it).
Call the same enforce_plan_access("label_assign", …) here (and on batch), or remove the client short-circuit from all Labels commands and rely on entitlement / server codes consistently.
| if not label_uuids: | ||
| raise click.UsageError("Pass at least one --label.") | ||
|
|
||
| client = require_auth() |
There was a problem hiding this comment.
Same gap as label assign: batch never calls enforce_plan_access while CRUD helpers do.
Add enforce_plan_access("label_batch", json_mode=json_mode) before require_auth(), or drop the free-tier short-circuit from the whole label group so assign/batch/list stay aligned.
| "paid_plan_required": "Organization Labels require a paid plan with Feature.LABELS enabled.", | ||
| "feature_not_available": "Organization Labels are not available on this plan.", | ||
| "guest_not_allowed": "Guests cannot manage organization Labels.", | ||
| "duplicate_name": "A label with this name already exists.", | ||
| "archived_label_not_assignable": "This label is archived and cannot be assigned.", | ||
| "permission_denied": "You don't have permission for this Labels action.", |
There was a problem hiding this comment.
AGENTS.md Rule 10 — Labels-specific copy registered under shared error codes.
permission_denied, duplicate_name, paid_plan_required, feature_not_available, and guest_not_allowed are generic codes. Putting Labels-only strings in the global ERROR_CODE_MESSAGES map means any command that receives those codes (forms, check-ins, workflows, future surfaces) will print "Organization Labels…" / "this Labels action".
Prefer Labels-scoped codes if the API has them, or pass code_overrides={...} into exit_for_api_error from label.py / featured.py only — that helper already documents this pattern for reused codes.
| fetch_all: bool = True, | ||
| limit: int | None = None, | ||
| meta: dict[str, Any] | None = None, | ||
| labels: list[str] | None = None, |
There was a problem hiding this comment.
Enrichment kwargs never reach the Click list commands.
list_forms / list_checkins / list_workflows accept labels / featured / prioritize_featured, but execute_form_list, form list, check-in listing, and workflow list still call the client without those kwargs — no --labels / --featured / --prioritize-featured flags exist. Failure mode: CORE-2362 filtering is unreachable from the CLI users run; only a direct DailyBotClient caller can use it.
Wire the flags through the list commands (and execute_form_list) or narrow the PR description to "client prep only" so agents do not assume dailybot form list --labels … works.
| ## Auth | ||
|
|
||
| Same session as the rest of the CLI (`dailybot login` or `DAILYBOT_API_KEY`). | ||
| Point at staging with `--api-url https://staging-api.dailybot.com` when testing |
There was a problem hiding this comment.
Staging hostname disagrees with the rest of the repo.
This skill tells agents to use https://staging-api.dailybot.com, while AGENTS.md, docs/CONFIGURATION.md, and other skills use https://staging.dailybot.com. Failure mode: agents copy the skill's URL and get connection failures against the documented staging host.
Align on the same host the rest of the CLI docs use (staging.dailybot.com), unless staging-api is the intentionally correct API host — then update the other docs instead.
| web picker on a form / check-in / automation row is a **replace-set**. Match | ||
| that with `dailybot label assign`. Bulk add/remove uses `dailybot label batch`. | ||
|
|
||
| Requires CLI from the personalized-labels branch (commands `label assign` and |
There was a problem hiding this comment.
Branch-specific install note will be stale on merge.
"Requires CLI from the personalized-labels branch" is fine for a WIP dogfood skill, but once this ships on main / PyPI it will mislead agents into looking for a feature branch.
Replace with a dailybot-cli >= X.Y.Z floor (or "requires a CLI build that includes label assign") once the release version is known.
| default=False, | ||
| ) | ||
| ): | ||
| raise SystemExit(0) |
There was a problem hiding this comment.
User abort exits with SystemExit(0) instead of EXIT_USER_ABORTED (7) from public_api_helpers.confirm_write.
Scripts that treat only non-zero as cancel cannot distinguish "deleted" from "user said no". Prefer raise SystemExit(EXIT_USER_ABORTED) for parity with other write confirms.
|
|
||
|
|
||
| def test_merge_dashboard_enrichment_query_serializes_csv_and_booleans() -> None: | ||
| params: dict = {} |
There was a problem hiding this comment.
AGENTS.md Rule 2 — missing type parameters.
| params: dict = {} | |
| params: dict[str, Any] = {} |
(Import Any from typing.) Also consider a twin assertion for list_checkins(...) — enrichment was added there too but only forms/workflows are covered.
| table.add_column("Archived", justify="center") | ||
| for label in labels: | ||
| usage: dict[str, Any] = label.get("usage") or {} | ||
| archived: str = "[green]yes[/green]" if label.get("is_archived") else "[dim]no[/dim]" |
There was a problem hiding this comment.
Archived=yes is styled [green] while active shows [dim]no. Green usually signals healthy/active in this CLI's tables, so archived rows look "good" at a glance.
Prefer [yellow]yes[/yellow] / [red]yes[/red] (or dim) for archived, and keep green for the active state if you want a positive cue.
Adds
dailybot labelanddailybot featuredso the public CLI can manage organization Labels and per-user Featured stars, and forwards that enrichment on list APIs. Companion to CORE-2362: https://linear.app/dailybot/issue/CORE-2362